﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Web.Mvc;
using OneWorks.Extension;

namespace OneWorks.Web.Common
{
    public class ActionTreeEntity
    {
        public string ID { get; set; }

        public string Parent { get; set; }

        public string Name { get; set; }

        public string Type { get; set; }

        public string Description { get; set; }

        public string Url { get; set; }
    }


    public class ParameterEntity
    {
        public string Name { get; set; }

        public string Description { get; set; }

        public string Type { get; set; }
    }

    public class MetadataHandler
    {
        private readonly Assembly assembly;

        public MetadataHandler(string assemblyFile = "")
        {
            if (string.IsNullOrEmpty(assemblyFile))
                assembly = Assembly.LoadFile(Assembly.GetExecutingAssembly().Location);
            assembly = Assembly.LoadFile(assemblyFile);
        }

        public List<ActionTreeEntity> GetActionTreeEntities()
        {
            List<ActionTreeEntity> actionTreeEntities = new List<ActionTreeEntity>();
            var namespaces = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Controller))).GroupBy(t => t.Namespace).ToList();
            foreach (IGrouping<string, Type> grouping in namespaces)
            {
                actionTreeEntities.Add(new ActionTreeEntity
                {
                    Parent = null,
                    ID = grouping.Key,
                    Name = GetNamespace(grouping.Key),
                    Type = "Namespace"
                });
                List<Type> types = assembly.GetTypes().Where(t => t.IsSubclassOf(typeof(Controller)) && t.Namespace == grouping.Key).ToList();
                foreach (Type type in types)
                {
                    actionTreeEntities.Add(new ActionTreeEntity
                    {
                        Parent = type.Namespace,
                        ID = type.Namespace + "." + type.Name,
                        Name = type.Name,
                        Type = "Controller",
                        Description = type.ToDescription()
                    });
                    MethodInfo[] methodInfos = type.GetMethods().Where(t => t.ReturnType == typeof(ActionResult) || t.ReturnType.IsSubclassOf(typeof(ActionResult))).ToArray();

                    actionTreeEntities.AddRange(methodInfos.Select(memberInfo => new ActionTreeEntity
                    {
                        Parent = type.Namespace + "." + type.Name,
                        ID = type.Namespace + "." + type.Name + "." + memberInfo.Name,
                        Name = memberInfo.Name,
                        Type = "ActionResult",
                        Description = memberInfo.ToDescription(),
                        Url = GetUrl(type.Namespace + "." + type.Name + "." + memberInfo.Name)
                    }));
                }
            }
            return actionTreeEntities;
        }

        public List<ParameterEntity> GetActionResultParams(string className, string methodName)
        {
            List<ParameterEntity> parameterEntities = new List<ParameterEntity>();
            Type type = assembly.GetType(className);
            var method = type.GetMethod(methodName);
            if (method == null) return new List<ParameterEntity>();
            var parameters = method.GetParameters();
            foreach (ParameterInfo parameterInfo in parameters)
            {
                if (parameterInfo.ParameterType.IsValueType || parameterInfo.ParameterType == typeof(string))
                {
                    parameterEntities.Add(new ParameterEntity
                    {
                        Name = parameterInfo.Name,
                        Type = parameterInfo.ParameterType.ToString()
                    });
                }
                else
                {
                    foreach (PropertyInfo property in parameterInfo.ParameterType.GetProperties())
                    {
                        if (!property.PropertyType.IsValueType && property.PropertyType != typeof(string))
                            continue;
                        parameterEntities.Add(new ParameterEntity
                        {
                            Name = parameterInfo.Name + "." + property.Name,
                            Type = property.PropertyType.ToString()
                        });
                    }
                }
            }
            return parameterEntities;
        }

        public string GetNamespace(string name)
        {
            var items = name.Split('.');
            if (items.Length <= 2)
                return name;
            return items[items.Length - 2] + "." + items[items.Length - 1];
        }

        public string GetUrl(string url)
        {
            var item = url.Split('.');
            var controller = item.First(t => t.Contains("Controller"));
            var action = item[item.Length - 1];
            if (url.Contains("Areas"))
            {
                return "/" + item[item.Length - 4] + "/" + item[item.Length - 2].Replace("Controller", "") + "/" + action;
            }
            return "/" + controller.Replace("Controller", "") + "/" + action;
        }
    }
}
